#include "iap.h"
#include "main.h"
#include <stdio.h>
#include <string.h>

void (*jump2app)(void);

void bootloader_display(void)
{
    printf("\r\n");
    printf("\r\n******************************************************************");
    printf("\r\n*       ____              __  __                __               *");
    printf("\r\n*      / __ )____  ____  / /_/ /___  ____ _____/ /__  _____      *");
    printf("\r\n*     / __  / __ \\/ __ \\/ __/ / __ \\/ __ `/ __  / _ \\/ ___/      *");
    printf("\r\n*    / /_/ / /_/ / /_/ / /_/ / /_/ / /_/ / /_/ /  __/ /          *");
    printf("\r\n*   /_____/\\____/\\____/\\__/_/\\____/\\__,_/\\__,_/\\___/_/           *");		
    printf("\r\n******************************************************************");
    printf("\r\n\r\n");
}

/* 0 for get signal; -1 for timeout */
int8_t get_iap_signal(uint32_t timeout)
{
    uint8_t signal;

    printf("input any character to enter iap!(%lds)\r", timeout / 1000);
    fflush(stdout);
    while(timeout)
    {
        if(uart_getchar(&signal, 1000) == 0)
        {
            return 0;
        }
        timeout -= 1000;
        printf("input any character to enter iap!(%lds)\r", timeout / 1000);
        fflush(stdout);
    }
    return -1;
}

void turn_to_app(void)
{
    /* 检查栈顶地址是否合法，也就是 APP 存放的起始地址是否合法 */
    if(((*(__IO uint32_t *)APPLICATION_ADDR) & 0x2FFE0000) == 0x20000000)
    {
        /* 屏蔽总中断，防止在程序跳转过程中，中断干扰出现异常 */
        __disable_irq();

        /* 关闭滴答定时器，复位到默认值 */
        SysTick->CTRL = 0;
        SysTick->LOAD = 0;
        SysTick->VAL = 0;

        /* 设置所有时钟到默认状态，使用HSI时钟 */
        HAL_RCC_DeInit();

        extern UART_HandleTypeDef huart1;
        __HAL_UART_DISABLE_IT(&huart1, UART_IT_IDLE);

        /* 关闭所有中断，清除所有中断挂起标志 */
        for (uint8_t i = 0; i < 8; i++)
        {
            NVIC->ICER[i]=0xFFFFFFFF;
            NVIC->ICPR[i]=0xFFFFFFFF;
        }
        
        /* 获取APP程序起始地址，代码区第二个字（起始地址+4   Flash 位置存储的数据） */
        jump2app = (void (*)(void)) (*(volatile uint32_t *)(APPLICATION_ADDR + 4));
        /* 初始化堆栈指针（用户代码区的第一个字用于存放栈顶地址） */
        __set_MSP(*(volatile uint32_t *)APPLICATION_ADDR);
        /* 跳转到用户代码区 */
        jump2app();
    }
    else
    {
        printf("APP address error\r\n");
    }
}

uint8_t FLASH_Erase_pages(uint32_t page, uint32_t NbPages)
{
    FLASH_EraseInitTypeDef eraseInitStruct;
    uint32_t pageError = 0;
    /* 擦除指定页 */
    eraseInitStruct.Banks = FLASH_BANK_1;
    eraseInitStruct.NbPages = NbPages;
    eraseInitStruct.Page = page;
    /* 擦除Flash */
    HAL_FLASH_Unlock();
    if(HAL_FLASHEx_Erase(&eraseInitStruct, &pageError) != HAL_OK)
    {
        printf("Erase failed @ page %ld\r\n", pageError);
        HAL_FLASH_Lock();
        return 1;
    }
    HAL_FLASH_Lock();
    return 0;
}

uint8_t FLASH_Program_8bytes(uint32_t address, uint64_t doubleWord)
{
    HAL_FLASH_Unlock();
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, doubleWord);
    HAL_FLASH_Lock();
    return 0;
}

/**
  * @brief  Calculate the number of pages
  * @param  Size: The image size
  * @retval The number of pages
  */
uint32_t FLASH_PageMask(uint32_t Size)
{
  uint32_t NbPage = 0x0;

  if ((Size % PAGE_SIZE) != 0)
  {
    NbPage = (Size / PAGE_SIZE) + 1;
  }
  else
  {
    NbPage = Size / PAGE_SIZE;
  }
  return NbPage;
}